home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 58189 / 58189.xpi / modules / CsFireController.jsm < prev    next >
Encoding:
Text File  |  2010-01-07  |  10.1 KB  |  288 lines

  1. /*
  2.  * This module contains all the operations that are provided by CsFire. This
  3.  * controller is used by the XPCOM service and actually handles all operations.
  4.  */
  5.  
  6. var EXPORTED_SYMBOLS = [ ];
  7. Components.utils.import("resource://csfiremodules/CsFireCommon.jsm");
  8.  
  9. CsFire.CsFireController = new function() {
  10.     this.authenticatedRequests = [];
  11. }
  12.  
  13. /*
  14.  * Accepts information about user interaction (destination and source URI)and 
  15.  * registers this with the data service.
  16.  */ 
  17. CsFire.CsFireController.registerUserInteraction = function(dst, src) {
  18.     CsFire.DataService.registerUserInteraction(dst, src);
  19. }
  20.  
  21. /*
  22.  * Accepts information from the browser about a request that is about to be 
  23.  * made (destination, source, content type and context (DOM)) and stores it with
  24.  * the data service.
  25.  */ 
  26. CsFire.CsFireController.registerLoadRequest = function(dstUri, srcUri, contentType, context) {
  27.     // Check if request is internal or not
  28.     if(!CsFire.HttpUtils.isRequestInternal(dstUri)) {
  29.         // Only external requests are stored
  30.         CsFire.DataService.registerLoadRequest(dstUri, srcUri, contentType, context);
  31.     }
  32. }
  33.  
  34. /*
  35.  * Accepts information about an HTTP request (the HTTP channel object) and stores
  36.  * it with the data service.
  37.  */ 
  38. CsFire.CsFireController.registerHttpRequest = function(httpChannel) {
  39.     // Check if request is internal or not
  40.     if(!CsFire.HttpUtils.isRequestInternal(httpChannel.URI)) {
  41.         // Only external requests are stored
  42.         CsFire.DataService.registerHttpRequest(httpChannel);
  43.     }
  44. }
  45.  
  46. /*
  47.  * Makes a decision about a request (using the destination URI and the HTTP
  48.  * channel). This will have a changing effect on the HTTP channel, based on the
  49.  * decision coming from the policies.
  50.  */ 
  51. CsFire.CsFireController.decideForURI = function(uri, httpChannel) {
  52.     // Check if request is internal or not
  53.     if(!CsFire.HttpUtils.isRequestInternal(uri)) {
  54.         var data = CsFire.DataService.getAndRemoveRequestData(uri);
  55.         var decision = CsFire.PolicyManager.decide(data);
  56.         this._handleDecision(decision, httpChannel);
  57.     }
  58. }
  59.  
  60. /*
  61.  * Processes an HTTP response (needed for working with authentication headers
  62.  * and redirects). This will not have an effect on the response, unless
  63.  * authentication needs to be done explicitly by the user.
  64.  */
  65. CsFire.CsFireController.registerHttpResponse = function(httpChannel) {
  66.     var stringUri = httpChannel.URI.prePath + httpChannel.URI.path;
  67.         
  68.     /*
  69.      * Clear the loading flag if set. This will cause firefox to think that
  70.      * authentication info is required, so the required info will be fetched.
  71.      * This can either be done from cache or from the user. If the info
  72.      * comes from the cache, we still have a CSRF issue. This needs to be
  73.      * handled, as discussed in stripAuthenticationFromRequest.
  74.      */
  75.     if(CsFire.PolicyManager.isEnabled() && httpChannel.loadFlags & Components.interfaces.nsIRequest.LOAD_ANONYMOUS) {
  76.         CsFire.Logger.debug("Disabling anonymous loading mode: " + stringUri);
  77.         httpChannel.loadFlags &= ~( Components.interfaces.nsIRequest.LOAD_ANONYMOUS);
  78.         
  79.         var authPath = CsFire.HttpUtils.getAuthenticatedPath(httpChannel.URI);
  80.         var status = httpChannel.responseStatus;
  81.         if(status == 401 || status == 407) {
  82.             // Access denied, so check if it's because we added a bogus header
  83.             try {
  84.                 var authHeader = httpChannel.getRequestHeader("authorization");
  85.                 if(authHeader.indexOf("csfire") != -1) {
  86.                     /*
  87.                      * Whoops, our fault, so mark the request as unauthenticated,
  88.                      * since Firefox has cleared it's cached credentials by now.
  89.                      */
  90.                     this.authenticatedRequests[authPath] = 0;
  91.                     CsFire.Logger.debug("Unmarking authenticated request: " + authPath);
  92.                 }
  93.             }
  94.             catch(e) {}
  95.         }
  96.         else {
  97.             /*
  98.              * Not an "access denied" status, so the request has been authorized.
  99.              * If we have indeed an authorization header, mark the path of the 
  100.              * request as authenticated.
  101.              */
  102.             try {
  103.                 httpChannel.getRequestHeader("authorization");
  104.                 // Header found!
  105.                 this.authenticatedRequests[authPath] = 1;
  106.                 CsFire.Logger.debug("Marking authenticated request: " + authPath);
  107.             }
  108.             catch(e) {}
  109.         }
  110.     }
  111.     
  112.     /*
  113.      * When 2 redirects using the Location header are chained, the originalURI
  114.      * stays the same for both redirects. In the following redirect case:
  115.      *         x.com --> y.com/foo/ --> y.com/bar/
  116.      * the httpChannel would contain the pair x.com --> y.com for the last
  117.      * redirect, which causes wrong policy enforcement actions.
  118.      *
  119.      * The solution to this problem is to make sure whenever a Location-
  120.      * redirect is used, the originalURI is set to the last redirector.
  121.      */
  122.     try {
  123.         httpChannel.getResponseHeader("Location");
  124.         try {
  125.             httpChannel.originalURI = httpChannel.URI;
  126.             CsFire.Logger.debug("Modified originalURI on redirect to: " + httpChannel.URI.prePath + "\n");
  127.         }
  128.         catch(e2) {
  129.             CsFire.Logger.error("Error modifying originalURI on redirect: " + e2);
  130.         }
  131.     }
  132.     catch(e1) {}
  133. }
  134.  
  135. /*
  136.  * Makes sure the decision of the policy is enforced on the HTTP channel. 
  137.  */
  138. CsFire.CsFireController._handleDecision = function(decision, channel) {
  139.     switch(decision.action) {
  140.         case CsFire.Policy.DISABLED:
  141.             break;
  142.         case CsFire.Policy.ACCEPT: 
  143.             break;
  144.         case CsFire.Policy.STRIP:
  145.             this._stripAuthentication(decision, channel);
  146.             break;
  147.         case CsFire.Policy.BLOCK:
  148.             channel.cancel(Components.results.NS_BINDING_ABORTED);
  149.             break;
  150.         default:
  151.             CsFire.Logger.error("Unknown decision type: " + decision.action);
  152.             break;
  153.     }
  154. }
  155.  
  156. /*
  157.  * Strips authentication credentials (Authentication headers and cookies) from 
  158.  * the HTTP channel, if this is specified by the decision.
  159.  */
  160. CsFire.CsFireController._stripAuthentication = function(decision, channel) {
  161.     var stringUri = channel.URI.prePath + channel.URI.path;
  162.         
  163.     if(decision.stripCookies != null && decision.stripCookies == true) {
  164.         this._removeCookies(channel, decision.cookieExceptions);
  165.     }
  166.     
  167.     if(decision.stripAuth != null && decision.stripAuth == true) {
  168.         /* TODO Enable stripping of authentication headers
  169.          * Code is disabled due to bug 537670
  170.          * see https://bugzilla.mozilla.org/show_bug.cgi?id=537670
  171.          */
  172.         //this._removeAuthHeaders(channel);
  173.     }
  174. }
  175.  
  176. /*
  177.  * Removes all cookies from the outgoing request, except for those specified
  178.  * in the exceptions list.
  179.  */
  180. CsFire.CsFireController._removeCookies = function(httpChannel, exceptions) {
  181.     if(exceptions == null) {
  182.         // Remove all cookies
  183.         httpChannel.setRequestHeader("cookie", "", false);
  184.     }
  185.     else {
  186.         try {
  187.             var cookies = httpChannel.getRequestHeader("cookie").split("; ");
  188.             var newCookies = "";
  189.             for(i in cookies) {
  190.                 try {
  191.                     var cookie = cookies[i];
  192.                     var cookieName = cookie.split("=")[0];
  193.                     if(exceptions[cookieName] != null) {
  194.                         if(newCookies != "") { newCookies += ";"; }
  195.                         newCookies += cookie;
  196.                     }
  197.                 }
  198.                 catch(e) { /* prevent one cookie error from killing all cookie operations */ }
  199.             }
  200.             httpChannel.setRequestHeader("cookie", newCookies, false);
  201.         }
  202.         catch(e) { /* no cookies present, saves us the trouble */ }
  203.     }
  204. }
  205.  
  206. /*
  207.  * Removes the authentication headers from the request. This uses a complicated
  208.  * mechanism, since they are probably not yet available (see comments in the
  209.  * code).
  210.  */
  211. CsFire.CsFireController._removeAuthHeaders = function(httpChannel) {
  212.     var stringUri = httpChannel.URI.prePath + httpChannel.URI.path;
  213.         
  214.     CsFire.Logger.debug("Enabling anonymous loading mode: " + stringUri);
  215.     
  216.     /*
  217.      * Since Firefox 3.5, the flag LOAD_ANONYMOUS can be used to prevent 
  218.      * Firefox from adding user-specific data to the request (auth headers, cookies).
  219.      * If we set it here, it's too late for cookies (wich we remove separately),
  220.      * but not for auth headers.
  221.      *
  222.      * The problem with HTTP auth is that if a 401 returns, Firefox will look
  223.      * for credentials while processing the *response*. If there are cached
  224.      * credentials (which there are in a CSRF attack), they will be used and
  225.      * the request will succeed (not what we want).
  226.      *
  227.      * To overcome this issue, we see whether the path (RFC2617 below) already has been
  228.      * authenticated before (set in processHttpResponse) and if so, change the
  229.      * authentication header to an invalid one. This will cause Firefox to
  230.      * discard the cached credentials and re-prompt the user when processing
  231.      * the response.
  232.      *
  233.      * RFC2617 states: A client SHOULD assume that all paths at or deeper than the depth of
  234.      * the last symbolic element in the path field of the Request-URI also
  235.      * are within the protection space specified by the Basic realm value of
  236.      * the current challenge.
  237.      */
  238.     httpChannel.loadFlags |=  Components.interfaces.nsIRequest.LOAD_ANONYMOUS;
  239.  
  240.  
  241.     // Check to see if we need to modify any response headers
  242.     try {
  243.         var authHeader = httpChannel.getRequestHeader("authorization");
  244.         // No failures here, so there already is an auth header present
  245.         var authPath = CsFire.HttpUtils.getAuthenticatedPath(httpChannel.URI);
  246.         if(this.authenticatedRequests[authPath] == 1) {
  247.             // We're dealing with an authenticated path, so modify the header
  248.             httpChannel.setRequestHeader("authorization", "Basic csfire=", false);
  249.             CsFire.Logger.debug("Cached authorization header, modifying: " + authPath);
  250.         }
  251.         else {
  252.             CsFire.Logger.debug("Fresh authorization header found, not touching");
  253.         }
  254.     }
  255.     catch(e) {
  256.         CsFire.Logger.debug("No authorization header found, not removing explicitly");
  257.     }
  258. }
  259.  
  260. /*
  261.  * This code will be executed on after a new installation or after an upgrade
  262.  */
  263. CsFire.CsFireController.performInstallAction = function() {
  264.     var version = CsFire.PreferenceUtils.getStringPreference("extensions.csfire.version", -1)
  265.     var current = Components.classes["@mozilla.org/extensions/manager;1"].getService(Components.interfaces.nsIExtensionManager).getItemForID("csfire@cs.kuleuven.be").version;
  266.     
  267.     if(version < current) {
  268.         CsFire.PreferenceUtils.setStringPreference("extensions.csfire.version", current);
  269.         if(version == -1) {
  270.             //This is a new installation
  271.             CsFire.Logger.info("New Installation detected, loading configuration ...");
  272.             
  273.             CsFire.Whitelist.addEntry("google.*", "google.*");
  274.             CsFire.Whitelist.addEntry("livefilestore.com", "skydrive.live.com");
  275.             CsFire.Whitelist.addEntry("skydrive.live.com", "livefilestore.com");
  276.         }
  277.         else {
  278.             //This is an update
  279.             CsFire.Logger.info("Update detected");
  280.             
  281.             /*
  282.              * To add update code, make sure the old version number is smaller
  283.              * than the version which needs the update code, then execute it.
  284.              */
  285.         }
  286.     }    
  287. }
  288.